-- Based on Skada's Healing.lua, Overhealing.lua, TotalHealing.lua, Absorbs.lua
-- Depends upon SkadaHealing and replaces SkadaAborbs module

-- NOTE: have to disable SkadaAbsorbs currently or both addons will double count absorbs

local L = LibStub("AceLocale-3.0"):GetLocale("SkadaHealAbsorbs", false)

local Skada = Skada

-- hacked out local crap here
local mod = Skada:NewModule("Healing and Absorbs")
local spellsmod = Skada:NewModule("Healing and Absorbs spell list")
local healedmod = Skada:NewModule("Healed and Absorbed players")


-- This bit shamelessly copied straight from RecountGuessedAbsorbs - thanks!
local AbsorbSpellDuration = 
{
	-- Death Knight
	[48707] = 5, -- Anti-Magic Shell (DK) Rank 1 -- Does not currently seem to show tracable combat log events. It shows energizes which do not reveal the amount of damage absorbed
	[51052] = 10, -- Anti-Magic Zone (DK)( Rank 1 (Correct spellID?)
		-- Does DK Spell Deflection show absorbs in the CL?
	[51271] = 20, -- Unbreakable Armor (DK)
	[77535] = 10, -- Blood Shield (DK)
	-- Druid
	[62606] = 10, -- Savage Defense proc. (Druid) Tooltip of the original spell doesn't clearly state that this is an absorb, but the buff does.
	-- Mage
	[11426] = 60, -- Ice Barrier (Mage) Rank 1
	[13031] = 60,
	[13032] = 60,
	[13033] = 60,
	[27134] = 60,
	[33405] = 60,
	[43038] = 60,
	[43039] = 60, -- Rank 8
	[6143] = 30, -- Frost Ward (Mage) Rank 1
	[8461] = 30, 
	[8462] = 30,  
	[10177] = 30,  
	[28609] = 30,
	[32796] = 30,
	[43012] = 30, -- Rank 7
	[1463] = 60, --  Mana shield (Mage) Rank 1
	[8494] = 60,
	[8495] = 60,
	[10191] = 60,
	[10192] = 60,
	[10193] = 60,
	[27131] = 60,
	[43019] = 60,
	[43020] = 60, -- Rank 9
	[543] = 30 , -- Fire Ward (Mage) Rank 1
	[8457] = 30,
	[8458] = 30,
	[10223] = 30,
	[10225] = 30,
	[27128] = 30,
	[43010] = 30, -- Rank 7
	-- Paladin
	[58597] = 6, -- Sacred Shield (Paladin) proc (Fixed, thanks to Julith)
	[86273] = 6, -- Illuminated Healing
	[88063] = 6, -- Guarded by the Light
	-- Priest
	[17] = 30, -- Power Word: Shield (Priest) Rank 1
	[592] = 30,
	[600] = 30,
	[3747] = 30,
	[6065] = 30,
	[6066] = 30,
	[10898] = 30,
	[10899] = 30,
	[10900] = 30,
	[10901] = 30,
	[25217] = 30,
	[25218] = 30,
	[48065] = 30,
	[48066] = 30, -- Rank 14
	[47509] = 12, -- Divine Aegis (Priest) Rank 1
	[47511] = 12,
	[47515] = 12, -- Divine Aegis (Priest) Rank 3 (Some of these are not actual buff spellIDs)
	[47753] = 12, -- Divine Aegis (Priest) Rank 1
	[54704] = 12, -- Divine Aegis (Priest) Rank 1
	[47788] = 10, -- Guardian Spirit  (Priest) (50 nominal absorb, this may not show in the CL)
	[62618] = 25, -- Power Word: Barrier
	[81781] = 25,
	-- Warlock
	[7812] = 30, -- Sacrifice (warlock) Rank 1
	[19438] = 30,
	[19440] = 30,
	[19441] = 30,
	[19442] = 30,
	[19443] = 30,
	[27273] = 30,
	[47985] = 30,
	[47986] = 30, -- rank 9
	[6229] = 30, -- Shadow Ward (warlock) Rank 1
	[11739] = 30,
	[11740] = 30,
	[28610] = 30,
	[47890] = 30,
	[47891] = 30, -- Rank 6
	-- Consumables
	[29674] = 86400, -- Lesser Ward of Shielding
	[29719] = 86400, -- Greater Ward of Shielding (these have infinite duration, set for a day here :P)
	[29701] = 86400,
	[28538] = 120, -- Major Holy Protection Potion
	[28537] = 120, -- Major Shadow
	[28536] = 120, --  Major Arcane
	[28513] = 120, -- Major Nature
	[28512] = 120, -- Major Frost
	[28511] = 120, -- Major Fire
	[7233] = 120, -- Fire
	[7239] = 120, -- Frost
	[7242] = 120, -- Shadow Protection Potion
	[7245] = 120, -- Holy
	[6052] = 120, -- Nature Protection Potion
	[53915] = 120, -- Mighty Shadow Protection Potion
	[53914] = 120, -- Mighty Nature Protection Potion
	[53913] = 120, -- Mighty Frost Protection Potion
	[53911] = 120, -- Mighty Fire
	[53910] = 120, -- Mighty Arcane
	[17548] = 120, --  Greater Shadow
	[17546] = 120, -- Greater Nature
	[17545] = 120, -- Greater Holy
	[17544] = 120, -- Greater Frost
	[17543] = 120, -- Greater Fire
	[17549] = 120, -- Greater Arcane
	[28527] = 15, -- Fel Blossom
	[29432] = 3600, -- Frozen Rune usage (Naxx classic)
	-- Item usage
	[36481] = 4, -- Arcane Barrier (TK Kael'Thas) Shield
	[57350] = 6, -- Darkmoon Card: Illusion
	[17252] = 30, -- Mark of the Dragon Lord (LBRS epic ring) usage
	[25750] = 15, -- Defiler's Talisman/Talisman of Arathor Rank 1
	[25747] = 15,
	[25746] = 15,
	[23991] = 15,
	[31000] = 300, -- Pendant of Shadow's End Usage
	[30997] = 300, -- Pendant of Frozen Flame Usage
	[31002] = 300, -- Pendant of the Null Rune
	[30999] = 300, -- Pendant of Withering
	[30994] = 300, -- Pendant of Thawing
	[31000] = 300, -- 
	[23506]= 20, -- Arena Grand Master Usage (Aura of Protection)
	[12561] = 60, -- Goblin Construction Helmet usage
	[31771] = 20, -- Runed Fungalcap usage
	[21956] = 10, -- Mark of Resolution usage
	[29506] = 20, -- The Burrower's Shell
	[4057] = 60, -- Flame Deflector
	[4077] = 60, -- Ice Deflector
	[39228] = 20, -- Argussian Compass (may not be an actual absorb)
	-- Item procs
	[27779] = 30, -- Divine Protection - Priest dungeon set 1/2  Proc
	[11657] = 20, -- Jang'thraze (Zul Farrak) proc
	[10368] = 15, -- Uther's Strength proc
	[37515] = 15, -- Warbringer Armor Proc
	[42137] = 86400, -- Greater Rune of Warding Proc
	[26467] = 30, -- Scarab Brooch proc
	[26470] = 8, -- Scarab Brooch proc (actual)
	[27539] = 6, -- Thick Obsidian Breatplate proc
	[28810] = 30, -- Faith Set Proc Armor of Faith
	[54808] = 12, -- Noise Machine proc Sonic Shield 
	[55019] = 12, -- Sonic Shield (one of these too ought to be wrong)
	[64411] = 15, -- Blessing of the Ancient (Val'anyr Hammer of Ancient Kings equip effect)
	[64413] = 8, -- Val'anyr, Hammer of Ancient Kings proc Protection of Ancient Kings
	-- Misc
	[40322] = 30, -- Teron's Vengeful Spirit Ghost - Spirit Shield
	-- Boss abilities
	[65874] = 15, -- Twin Val'kyr's Shield of Darkness 175000
	[67257] = 15, -- 300000
	[67256] = 15, -- 700000
	[67258] = 15, -- 1200000
	[65858] = 15, -- Twin Val'kyr's Shield of Lights 175000
	[67260] = 15, -- 300000
	[67259] = 15, -- 700000
	[67261] = 15, -- 1200000
}

local shields = {}

local function AuraApplied(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...)
	local spellId, spellName, spellSchool, auraType = ...
	if AbsorbSpellDuration[spellId] then
	
		shields[dstName] = shields[dstName] or {}
		shields[dstName][spellId] = shields[dstName][spellId] or {}
		shields[dstName][spellId][srcName] = timestamp + AbsorbSpellDuration[spellId]
		
	end
end

local function AuraRemoved(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...)
	local spellId, spellName, spellSchool, auraType = ...
	if AbsorbSpellDuration[spellId] then
	
		if shields[dstName] and shields[dstName][spellId] and shields[dstName][spellId][dstName] then
			-- As advised in RecountGuessedAbsorbs, do not remove shields straight away as an absorb can come after the aura removed event.
			shields[dstName][spellId][dstName] = timestamp + 0.1
		end
	
	end
end

local function log_absorb(set, srcName, dstName, amount, spell_id)
	-- Get the player
	local player = Skada:get_player(set, UnitGUID(srcName), UnitName(srcName))
	if player then
		-- add the absorb amount to the player's current healed total
		player.absorbTotal = player.absorbTotal + amount
		
		-- Also add to the set total heals
		set.absorbTotal = set.absorbTotal + amount
		
		-- Create recipient if it does not exist.
		if not player.absorbed[dstName] then
			player.absorbed[dstName] = {class = select(2, UnitClass(dstName)), amount = 0}
		end
		
		-- Add to recipient absorbs.
		player.absorbed[dstName].amount = player.absorbed[dstName].amount + amount
		
		-- Create spell if it does not exist.
		local spellName = GetSpellInfo(spell_id)
		if not player.absorbSpells[spellName] then
			player.absorbSpells[spellName] = {id = spell_id, name = spellName, hits = 0, healing = 0, overhealing = 0, critical = 0, min = 0, max = 0}
		end
		
		-- Get the spell from player.
		local spell = player.absorbSpells[spellName]
		
		spell.healing = spell.healing + amount
		spell.hits = (spell.hits or 0) + 1

		--NOTE The min code here is just copied from Skada's Healing.lua, but it doesn't work in either spot, needs a better implementation
		if not spell.min or amount < spell.min then
			spell.min = amount
		end
		if not spell.max or amount > spell.max then
			spell.max = amount
		end
		
		--DEBUG
		--local channelID = GetChannelName("SkadaDebug");
		--if( channelID > 0 and set == Skada.current and amount > 15000 ) then
		--	SendChatMessage("Absorb: source["..srcName.."] -> destination["..dstName.."] || spell["..spellName.."] -> value["..amount.."]","CHANNEL", nil,channelID)
		--end
	end
end

local function consider_absorb(amount, dstName, timestamp)
	local mintime = nil
	local found_shield_src
	local spell_id
	for shield_id, spells in pairs(shields[dstName]) do
		for shield_src, ts in pairs(spells) do
			if ts - timestamp > 0 and (mintime == nil or ts - timestamp < mintime) then
				found_shield_src = shield_src
				spell_id = shield_id
			end
		end
	end

	if found_shield_src then
		log_absorb(Skada.current, found_shield_src, dstName, amount,spell_id)
		log_absorb(Skada.total, found_shield_src, dstName, amount,spell_id)
	end
end

local function SwingDamage(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...)
	local samount, soverkill, sschool, sresisted, sblocked, absorbed, scritical, sglancing, scrushing = ...
	if absorbed and absorbed > 0 and dstName and shields[dstName] and srcName then
		--Skada:Print(dstName.." absorbed "..absorbed.." from "..srcName.." (SWING)")
		consider_absorb(absorbed, dstName, timestamp)
	end
end

local function SpellDamage(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...)
	local spellId, spellName, spellSchool, samount, soverkill, sschool, sresisted, sblocked, absorbed, scritical, sglancing, scrushing = ...
	if absorbed and absorbed > 0 and dstName and shields[dstName] and srcName then
		--Skada:Print(dstName.." absorbed "..absorbed.." from "..srcName.." (SPELL)")
		consider_absorb(absorbed, dstName, timestamp)
	end
end

local function SpellMissed(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...)
	local spellId, spellName, spellSchool, misstype, isOffHand, absorbed = ...
	if misstype == "ABSORB" and absorbed and absorbed > 0 and dstName and shields[dstName] and srcName then
		--Skada:Print(dstName.." absorbed "..absorbed.." from "..srcName.." (MISS)")
		consider_absorb(absorbed, dstName, timestamp)
	end
end

local function SwingMissed(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...)
	local missType, isOffHand, amount = ...
	if missType == "ABSORB" and amount and amount > 0 and dstName and shields[dstName] and srcName then
		consider_absorb(amount, dstName, timestamp)
	end
end

local function getHPS(set, player)
	local totaltime = Skada:PlayerActiveTime(set, player)
	
	return (player.healing+player.absorbTotal) / math.max(1,totaltime)
end

function mod:Update(win, set)
	local nr = 1
	local max = 0

	for i, player in ipairs(set.players) do
		if ((player.healing+player.absorbTotal) > 0) then
		
			local d = win.dataset[nr] or {}
			win.dataset[nr] = d
			
			d.id = player.id
			d.value = player.healing + player.absorbTotal
			d.label = player.name
			d.valuetext = Skada:FormatValueText(
				Skada:FormatNumber(player.healing+player.absorbTotal),
				self.metadata.columns.Healing,
				string.format("%02.1f", getHPS(set, player)),
				self.metadata.columns.HPS,
				(" (%02.1f%%)"):format((player.healing+player.absorbTotal) / (set.healing+set.absorbTotal) * 100),
				self.metadata.columns.Percent
			)
			d.class = player.class

			if((player.healing+player.absorbTotal) > max) then
				max = player.healing+player.absorbTotal
			end
			
			nr = nr + 1
		end
	end
	
	win.metadata.maxvalue = max
end

local function spell_tooltip(win, id, label, tooltip)
	local player = Skada:find_player(win:get_selected_set(), spellsmod.playerid)
	if player then
		local spell = player.healingspells[label]
		--if not spell then spell = player.absorbSpells[label] end
		
		if spell then
			tooltip:AddLine(player.name.." - "..label)
			if spell.max and spell.min then
				--tooltip:AddDoubleLine(L["Minimum hit:"], Skada:FormatNumber(spell.min), 255,255,255,255,255,255)
				tooltip:AddDoubleLine(L["Maximum hit:"], Skada:FormatNumber(spell.max), 255,255,255,255,255,255)
			end

			tooltip:AddDoubleLine(L["Average hit:"], Skada:FormatNumber(spell.healing / spell.hits), 255,255,255,255,255,255)
			if spell.hits then
				tooltip:AddDoubleLine(L["Critical"]..":", ("%02.1f%%"):format(spell.critical / spell.hits * 100), 255,255,255,255,255,255)
			end
			if spell.hits then
				tooltip:AddDoubleLine(L["Overhealing"]..":", ("%02.1f%%"):format(spell.overhealing / (spell.overhealing + spell.healing) * 100), 255,255,255,255,255,255)
			end
			if spell.hits and spell.absorbed then
				tooltip:AddDoubleLine(L["Absorbed"]..":", ("%02.1f%%"):format(spell.absorbed / (spell.overhealing + spell.healing) * 100), 255,255,255,255,255,255)
			end
		end
	end
end

function spellsmod:Enter(win, id, label)
	spellsmod.playerid = id
	spellsmod.title = label..L["'s Healing & Absorbs"]
end

-- Spell view of a player.
function spellsmod:Update(win, set)
	-- View spells for this player.
		
	local player = Skada:find_player(set, self.playerid)
	local nr = 1
	local max = 0
	
	if player then
		local healAbsorbSpells = {}
	
		-- copy the healingspells table
		for spellname, spell in pairs(player.healingspells) do healAbsorbSpells[spellname] = spell end

		-- merge in the absorbSpells table
		for spellname, spell in pairs(player.absorbSpells) do healAbsorbSpells[spellname] = spell end

		for spellname, spell in pairs(healAbsorbSpells) do
		
			local d = win.dataset[nr] or {}
			win.dataset[nr] = d
			
			d.id = spell.id
			d.label = spell.name
			d.value = spell.healing
			d.valuetext = Skada:FormatValueText(
											Skada:FormatNumber(spell.healing), self.metadata.columns.Healing,
											string.format("%02.1f%%", spell.healing / (player.healing + player.absorbTotal) * 100), self.metadata.columns.Percent
										)
			d.icon = select(3, GetSpellInfo(spell.id))
			
			if spell.healing > max then
				max = spell.healing
			end
			
			nr = nr + 1
		end
	end
	
	win.metadata.maxvalue = max
end

function healedmod:Enter(win, id, label)
	healedmod.playerid = id
	healedmod.title = L["Healed by"].." "..label
end

-- Healed players view of a player.
function healedmod:Update(win, set)
	local player = Skada:find_player(set, healedmod.playerid)
	local nr = 1
	local max = 0
	
	if player then
		local healAbsorbs = {}
		
		-- copy the heal table
		for name, heal in pairs(player.healed) do healAbsorbs[name] = heal end

		-- merge in the absorb table
		for name, heal in pairs(player.absorbed) do
			if not healAbsorbs[name] then
				healAbsorbs[name] = heal
			else
				healAbsorbs[name].amount = healAbsorbs[name].amount + heal.amount
			end
		end
		for name, heal in pairs(healAbsorbs) do
			if heal.amount > 0 then
		
				local d = win.dataset[nr] or {}
				win.dataset[nr] = d
				
				d.id = name
				d.label = name
				d.value = heal.amount
				d.class = heal.class
				d.valuetext = Skada:FormatValueText(
												Skada:FormatNumber(heal.amount), self.metadata.columns.Healing,
												string.format("%02.1f%%", heal.amount / (player.healing+player.absorbTotal) * 100), self.metadata.columns.Percent
											)
				if heal.amount > max then
					max = heal.amount
				end
				
				nr = nr + 1
			end
		end
	end
	
	win.metadata.maxvalue = max
end

function mod:OnEnable()
mod.metadata = {showspots = true, click1 = spellsmod, click2 = healedmod, columns = {Healing = true, HPS = true, Percent = true}}
spellsmod.metadata 	= {tooltip=spell_tooltip, columns = {Healing = true, Percent = true}}
healedmod.metadata 	= {showspots = true, columns = {Healing = true, Percent = true}}

Skada:RegisterForCL(AuraApplied, 'SPELL_AURA_REFRESH', {src_is_interesting_nopets = true})
Skada:RegisterForCL(AuraApplied, 'SPELL_AURA_APPLIED', {src_is_interesting_nopets = true})
Skada:RegisterForCL(AuraRemoved, 'SPELL_AURA_REMOVED', {src_is_interesting_nopets = true})
Skada:RegisterForCL(SpellDamage, 'DAMAGE_SHIELD', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SpellDamage, 'SPELL_DAMAGE', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SpellDamage, 'SPELL_PERIODIC_DAMAGE', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SpellDamage, 'SPELL_BUILDING_DAMAGE', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SpellDamage, 'RANGE_DAMAGE', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SwingDamage, 'SWING_DAMAGE', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SpellMissed, 'SPELL_MISSED', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SpellMissed, 'SPELL_PERIODIC_MISSED', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SpellMissed, 'SPELL_BUILDING_MISSED', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SpellMissed, 'RANGE_MISSED', {dst_is_interesting_nopets = true})
Skada:RegisterForCL(SwingMissed, 'SWING_MISSED', {dst_is_interesting_nopets = true})

	Skada:AddMode(self)
end

function mod:OnDisable()
	Skada:RemoveMode(self)
end

function mod:AddToTooltip(set, tooltip)
	local endtime = set.endtime
	if not endtime then
		endtime = time()
	end
	local raidhps = (set.healing+set.absorbTotal) / (endtime - set.starttime + 1)
 	GameTooltip:AddDoubleLine(L["HPS"], ("%02.1f"):format(raidhps), 1,1,1)
end

function mod:GetSetSummary(set)
	return Skada:FormatNumber(set.healing+set.absorbTotal)
end

-- Called by Skada when a new player is added to a set.
function mod:AddPlayerAttributes(player)
	if not player.absorbed then
		player.absorbed = {} -- Stored absorbs per recipient
	end
	if not player.absorbTotal then
		player.absorbTotal = 0 -- Total absorbs
		player.absorbSpells = {} -- Absorbed spells
	end
end

-- Called by Skada when a new set is created.
-- Conveniently this is when a new fight starts, so we can clear our shield cache.
function mod:AddSetAttributes(set)
	if not set.absorbTotal then
		set.absorbTotal = 0
	end
	shields = {}
end
